
import java.lang.Exception;


/**
 * Represents a collection of communities
 */
public class CommunityCollection {

        private Community _communities[]; //Community is an implementation of a linked-list, didn't use the ready made java ones because don't let u merge two linked lists
        private int _numCommunities;
        private Double _modularity;
        
        /**
         * Constructs a community collection with each vertex being its own community
         * @param num_vertices the number of vertices (which is also the number of communities as each vertex is put in its own community)
         */
        public CommunityCollection(int num_vertices)
        {
                int i;

                //Creates the initial community collection, each element is
                //a list, and initially each list has just one element,
                //which is a single vertex (each list represents a community

                _communities = new Community[num_vertices];

                for(i=0;i<num_vertices;i++)
                {
                        _communities[i] = new Community(i);
                }
                
                _numCommunities = num_vertices;
        }
        
        /**
         * A deep copy constructor
         * @param c the community collection to clone
         */
        public CommunityCollection(CommunityCollection c)
        {
            this._communities = new Community[c._communities.length];
            this._numCommunities = c._numCommunities;
            this._modularity = c._modularity;
            
            for (int i=0;i<c._communities.length;i++)
            {
                if (c._communities[i] != null)
                {
                    this._communities[i] = new Community(c._communities[i]);
                }
            }
        }
        
        public void setModularity(double param_modularity)
        {
            _modularity = param_modularity;
        }
        
        /**
         * Gets the modularity of the partition specified by this community collection,
         * this must be set explicitly as a community collection object only encapsulates vertices
         * and has no way of deducing the modularity
         * @return the modularity of the partition specified by this community collection, 
         * @throws NullPointerException thrown when the modularity has not been set explicitly since the last time the community collection was changed
         */
        public double getModularity() throws NullPointerException
        {
            if (_modularity == null)
                throw new NullPointerException("Modularity for a community collection must be set explicitly, only a GraphPartition allows for on-the-fly evaluation of modularity");
                        
            return _modularity;
        }
        
        /**
         * Merges two communities
         * @param index1 the index of the first community
         * @param index2 the index of the second community
         */
        public void mergeCommunities(int index1, int index2)
        {
            if (index1 < index2)
            {
                    _communities[index1].attach(_communities[index2]);
                    _communities[index2] = null;
                    _numCommunities--;
            }
            else if (index2 < index1)
            {
                    _communities[index2].attach(_communities[index1]);
                    _communities[index1] = null;
                    _numCommunities--;
            }
            else
            {
                    throw new IllegalArgumentException("communities given are the same, need two DIFFERENT communities to merge");
            }

            _modularity = null; //This is to prevent a case where the modularity is left-over from a previous partition before a merge
        }
        
        /**
         * @return the number of communities in the collection
         */
        public int getNumCommunities()
        {
            return _numCommunities;
        }
            
        /**
         * Prints the communities in the collection, for each community it outputs the indices of all its vertices
         */
        public void print()
        {
            int cnt=1;
            
            for (int i=0;i<this._communities.length;i++)
            {
                if (_communities[i] != null)
                {
                    System.out.print("Community " + cnt + ": ");
                    _communities[i].print();
                    System.out.print("\n");
                    cnt++;
                }
                
            }

        }
        
        /**
         * Returns an array of Community objects according to the communities in the collection
         * @return an array of Community objects, as specified
         */
        public Community[] toArray()
        {
            Community[] ret_array = new Community[_numCommunities];
            int cnt=0;
            
            for (int i=0;i<this._communities.length;i++)
            {
                if (_communities[i] != null)
                {
                    ret_array[cnt] = _communities[i];
                    cnt++;
                }
            }
            
            return ret_array;
        }

}
